package com.zhou.common.quartz.test.timewheel;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import com.zhou.base.components.bs.common.thread.LoggingThreadException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * @author xianfeng
 * @date 2023/12/16 9:55
 * @description: 定时器实现
 */

@Data
@Slf4j
public class TimerLauncher implements Timer {

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    /**
     * 核心线程数 = CPU核心数 + 1
     */
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    /**
     * 线程池最大线程数 = CPU核心数 * 2 + 1
     */
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    /**
     * 非核心线程闲置时超时1s
     */
    private static final int KEEP_ALIVE = 1;
    /**
     * 底层时间轮
     */
    private TimeWheel timeWheel;
    /**
     * 一个Timer只有一个延时队列
     */
    private DelayQueue<TimerTaskList> delayQueue = new DelayQueue<>();
    /**
     * 过期任务执行线程
     */
    private ExecutorService workerThreadPool;
    /**
     * 轮询delayQueue获取过期任务线程
     */

    private ExecutorService bossThreadPool;

    private int step = 1;

    /**
     * @return 当前时间
     */


    public TimerLauncher() {
        ThreadFactory workerFactory = ThreadFactoryBuilder.create()
                .setUncaughtExceptionHandler(new LoggingThreadException())
                .setNamePrefix("timeWheel-worker")
                .build();
        workerThreadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
                new LinkedBlockingQueue(1000), workerFactory, new ThreadPoolExecutor.CallerRunsPolicy());
        ThreadFactory bossFactory = ThreadFactoryBuilder.create()
                .setUncaughtExceptionHandler(new LoggingThreadException())
                .setNamePrefix("timeWheel-boss")
                .build();
        bossThreadPool = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue(1), bossFactory);

        this.timeWheel = new TimeWheel(step, 20, delayQueue);

        // 20ms推动一次时间轮运转
        //submit 会将异常也封装给Future 在future.get()时抛出
//        this.bossThreadPool.submit(() -> {
        this.bossThreadPool.execute(() -> {
            while (true) {
/*                try {
                    this.advanceClock(20);
//                } catch (Exception e) {
                } catch (Throwable e) {
                    log.error("", e);
                }*/
                /*if (1 == 1) {
                    throw BaseExceptionBuilder.message("测试错误");
                }*/
                this.advanceClock(step);
            }
        });
    }

    public static TimerLauncher common() {
        return InstanceHolder.INSTANCE;
    }

    public void addTimerTaskEntry(TimerTaskEntry entry) {
        if (!timeWheel.add(entry)) {
            // 任务已到期
            TimerTask timerTask = entry.getTimerTask();
//            log.info("=====任务:{} 已到期,准备执行============", timerTask.getDesc());
            workerThreadPool.execute(timerTask);
        }
    }

    @Override
    public void add(TimerTask timerTask) {
        log.info("=======添加任务开始====task:{}", timerTask.getDesc());
//        TimerTaskEntry entry = new TimerTaskEntry(timerTask, timerTask.getDelayMs() + System.currentTimeMillis());
        TimerTaskEntry entry = new TimerTaskEntry(timerTask, timerTask.getDelayMs() + SystemClock.now());
        timerTask.setTimerTaskEntry(entry);
        addTimerTaskEntry(entry);
    }

    /**
     * 推动指针运转获取过期任务
     *
     * @param timeout 时间间隔
     * @return
     */
    @Override
    public synchronized void advanceClock(long timeout) {
        try {
            ThreadUtil.sleep(step);
            //不停的放入同一个
            TimerTaskList bucket = delayQueue.poll(step, TimeUnit.SECONDS);
            if (bucket != null) {
                TimerTaskEntry next = bucket.getRoot().getNext();
                log.debug("11111,{},{},{},count={},{}", bucket.getLevel(), bucket.getBucketIndex(), next.getExpireMs(), bucket.getTaskCount(), bucket);
                // 推进时间
                //timeWheel.advanceLock(SystemClock.now());
                // 执行过期任务(包含降级)
                bucket.clear(this::addTimerTaskEntry);
            }
        } catch (InterruptedException e) {
            log.error("advanceClock error");
        }
    }

    @Override
    public int size() {
        return 10;
    }

    @Override
    public void shutdown() {
        this.bossThreadPool.shutdown();
        this.workerThreadPool.shutdown();
        this.timeWheel = null;
    }

    private static class InstanceHolder {
        public static final TimerLauncher INSTANCE = new TimerLauncher();
    }

}